﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows;
using System.Windows.Controls;
using BindableDataGrid.Data;
using SilveR.DataValidators;
using SilveR.GUIValidators;

namespace SilveR.Views.StatsViews
{
    public partial class SummaryStats : UserControl, IAnalysis
    {
        private SummaryStatsVariables summaryStatsVariables;

        public SummaryStats()
        {
            InitializeComponent();

            summaryStatsVariables = new SummaryStatsVariables();
            this.DataContext = summaryStatsVariables;
        }

        private void oneItem_DragOverOrDrop(object sender, Microsoft.Windows.DragEventArgs e)
        {
            ListBoxDragDropTarget target = (ListBoxDragDropTarget)sender;
            ListBox lstBox = (ListBox)target.Content;

            if (lstBox.Items.Count == 1)
            {
                e.Effects = Microsoft.Windows.DragDropEffects.None;
                e.Handled = true;
            }
        }

        private void Target_ItemDragCompleted(object sender, ItemDragEventArgs e)
        {
            //ensure that the available variables stay sorted...
            SortableObservableCollection<ColumnInfo> availableVariables = (SortableObservableCollection<ColumnInfo>)lstAvailableVariables.ItemsSource;
            availableVariables.Sort(new ColumnIndexComparer());
        }

        private void cboData_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DataHandler dh = (DataHandler)cboData.SelectedItem;

            if(dh != null)
                summaryStatsVariables.AvailableVariables = dh.GetVariableList();
        }

        private void CheckBox_CheckChanged(object sender, RoutedEventArgs e)
        {
            numConfidenceLimits.IsEnabled = !summaryStatsVariables.ConfidenceLimitsSelected;
        }


        #region IAnalysis Members

        public bool ValidateGUI()
        {
            //check that something has been selected
            //first just check to ensure that the user has actually selected something to output!
            if (!summaryStatsVariables.MeanSelected && !summaryStatsVariables.NSelected && !summaryStatsVariables.StDevSelected && !summaryStatsVariables.VariancesSelected && !summaryStatsVariables.StErrSelected && !summaryStatsVariables.MinAndMaxSelected && !summaryStatsVariables.MedianQuartileSelected && !summaryStatsVariables.CoefficientVariationSelected && !summaryStatsVariables.NormalProbabilityPlotSelected)
            {
                MessageBox.Show("You have not selected anything to output!", "Validate Data", MessageBoxButton.OK);
                return false;
            }

            List<ValidationResult> validationResult = new List<ValidationResult>();
            bool validatedOK = Validator.TryValidateObject(summaryStatsVariables, new ValidationContext(summaryStatsVariables, null, null), validationResult, true);

            if (!validatedOK)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(validationResult);
                vmw.Show();
            }

            return validatedOK;
        }

        public event DataValidatedEventHandler DataValidated;

        public void ValidateData()
        {
            SummaryStatsValidator npv = new SummaryStatsValidator(summaryStatsVariables);
            bool result = npv.Validate();

            if (npv.WarningMessages.Count > 0)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(npv.WarningMessages);
                vmw.Closed += new EventHandler(vmw_Closed);
                vmw.Show();
            }
            else
            {
                if (DataValidated != null)
                    DataValidated(this, new DataValidatedEventArgs(result));
            }
        }

        private void vmw_Closed(object sender, EventArgs e)
        {
            ValidationMessagesWindow vmw = (ValidationMessagesWindow)sender;

            if (DataValidated != null)
                DataValidated(this, new DataValidatedEventArgs(vmw.DialogResult.Value));
        }

        public string[] ExportData()
        {
            DataTable dtNew = this.DataHandler.DataTable.Copy();
            dtNew.Name = "ExportData";

            //Get the response, treatment and covariate columns by removing all other columns from the new datatable
            foreach (DataColumn col in this.DataHandler.DataTable.Columns)
            {
                if (!summaryStatsVariables.ResponseColumnNames.Contains(col.ColumnName) && summaryStatsVariables.FirstCatFactor != col.ColumnName && summaryStatsVariables.SecondCatFactor != col.ColumnName && summaryStatsVariables.ThirdCatFactor != col.ColumnName && summaryStatsVariables.FourthCatFactor != col.ColumnName)
                {
                    dtNew.Columns.Remove(col);
                }
            }

            //ensure that all data is trimmed
            dtNew.TrimAllDataInDataTable();

            //Generate a "catfact" column from the CatFactors!
            DataColumn catFactor = new DataColumn("catfact");
            dtNew.Columns.Add(catFactor);

            foreach (DataRow r in dtNew.Rows) //go through each row...
            {
                string firstCatFactorValue = null;
                string secondCatFactorValue = null;
                string thirdCatFactorValue = null;
                string fourthCatFactorValue = null;

                if (!String.IsNullOrEmpty(summaryStatsVariables.FirstCatFactor))
                    firstCatFactorValue = r[summaryStatsVariables.FirstCatFactor].ToString() + " ";

                if (!String.IsNullOrEmpty(summaryStatsVariables.SecondCatFactor))
                    secondCatFactorValue = r[summaryStatsVariables.SecondCatFactor].ToString() + " ";

                if (!String.IsNullOrEmpty(summaryStatsVariables.ThirdCatFactor))
                    thirdCatFactorValue = r[summaryStatsVariables.ThirdCatFactor].ToString() + " ";

                if (!String.IsNullOrEmpty(summaryStatsVariables.FourthCatFactor))
                    fourthCatFactorValue = r[summaryStatsVariables.FourthCatFactor].ToString();

                string catFactorValue = firstCatFactorValue + secondCatFactorValue + thirdCatFactorValue + fourthCatFactorValue;
                r["catfact"] = catFactorValue.Trim(); //copy the cat factor to the new column
            }

            //Now do transformations
            foreach (string resp in summaryStatsVariables.ResponseColumnNames)
            {
                dtNew.TransformColumn(resp, summaryStatsVariables.Transformation);
            }

            return dtNew.GetCSVArray();
        }

        public DataHandler DataHandler
        {
            get { return summaryStatsVariables.SelectedDataHandler; }
        }

        public Dictionary<string, string> GetAnalysisArguments()
        {
            Dictionary<string, string> arguments = new Dictionary<string, string>();

            //get responses, comma separated
            string responses = null;
            foreach (ColumnInfo resp in summaryStatsVariables.Responses)
            {
                responses = responses + "," + resp.ColumnName;
            }

            arguments.Add("Responses", ArgumentConverters.ConvertIllegalChars(responses.TrimStart(','))); //4

            //get transforms
            arguments.Add("Transformation", "\"" + summaryStatsVariables.Transformation + "\""); //5

            //1st cat factor
            arguments.Add("First Cat Factor", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(summaryStatsVariables.FirstCatFactor))); //6

            //2nd cat factor
            arguments.Add("Second Cat Factor", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(summaryStatsVariables.SecondCatFactor))); //7

            //3rd cat factor
            arguments.Add("Third Cat Factor", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(summaryStatsVariables.ThirdCatFactor))); //8

            //4th cat factor
            arguments.Add("Fourth Cat Factor", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(summaryStatsVariables.FourthCatFactor))); //9

            //Mean
            arguments.Add("Mean Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.MeanSelected)); //10

            //N
            arguments.Add("N Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.NSelected)); //11

            //St Dev
            arguments.Add("StDev Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.StDevSelected)); //12

            //Variances
            arguments.Add("Variances Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.VariancesSelected)); //13

            //St Err
            arguments.Add("StErr Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.StErrSelected)); //14

            //Min and Max
            arguments.Add("Min And Max Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.MinAndMaxSelected)); //15

            //Median Quartile
            arguments.Add("Median Quartile Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.MedianQuartileSelected)); //16

            //Coefficient Variation
            arguments.Add("Coefficient Of Var Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.CoefficientVariationSelected)); //17

            //Confidence Limits
            arguments.Add("Confidence Limits Selected", ArgumentConverters.GetYesOrNo(summaryStatsVariables.ConfidenceLimitsSelected)); //18

            //Confidence Limits
            arguments.Add("Confidence Limits", summaryStatsVariables.ConfidenceLimits.ToString()); //19

            //Normal Probability Plots
            arguments.Add("Normal Probability Plot", ArgumentConverters.GetYesOrNo(summaryStatsVariables.NormalProbabilityPlotSelected));

            //By Categories and Overall
            arguments.Add("By Categories And Overall", ArgumentConverters.GetYesOrNo(summaryStatsVariables.ByCategoriesAndOverall)); //20

            return arguments;
        }

        public string AnalysisName
        {
            get { return "Summary Statistics Analysis"; }
        }

        public string ScriptName
        {
            get { return "SummaryStats.R"; }
        }

        public void UpdateDataHandlers()
        {
            //remember the current data handler before updating the available variables so that it can be selected later...
            DataHandler currentDataHandler = summaryStatsVariables.SelectedDataHandler;

            App app = (App)Application.Current;
            summaryStatsVariables.AvailableDataHandlers = app.DataHandlers;

            //reselect the current handler (if any)
            summaryStatsVariables.SelectedDataHandler = currentDataHandler;
        }

        #endregion
    }


    public class SummaryStatsVariables : INotifyPropertyChanged
    {
        public SummaryStatsVariables()
        {
            App app = (App)Application.Current;
            availableDataHandlers = app.DataHandlers;
        }

        private DataHandler selectedDataHandler;
        public DataHandler SelectedDataHandler
        {
            get { return selectedDataHandler; }
            set
            {
                selectedDataHandler = value;
                NotifyPropertyChanged("SelectedDataHandler");
            }
        }

        private ObservableCollection<DataHandler> availableDataHandlers = new ObservableCollection<DataHandler>();
        public ObservableCollection<DataHandler> AvailableDataHandlers
        {
            get
            {
                return availableDataHandlers;
            }

            set
            {
                availableDataHandlers = value;
                NotifyPropertyChanged("AvailableDataHandlers");
            }
        }

        private ObservableCollection<ColumnInfo> availableVariables = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> AvailableVariables
        {
            get { return availableVariables; }
            set
            {
                availableVariables = value;
                NotifyPropertyChanged("AvailableVariables");
            }
        }

        private ObservableCollection<ColumnInfo> responses = new ObservableCollection<ColumnInfo>();
        [CustomValidation(typeof(SummaryStatsGUIValidator), "CheckListVarSet")]
        [Display(Name = "Responses")]
        public ObservableCollection<ColumnInfo> Responses
        {
            get { return responses; }
            set
            {
                responses = value;
                NotifyPropertyChanged("Responses");
            }
        }

        public List<string> ResponseColumnNames
        {
            get
            {
                List<string> names = new List<string>();

                foreach (ColumnInfo ci in responses) names.Add(ci.ColumnName);
                return names;
            }
        }

        private string transformation = "None";
        public string Transformation
        {
            get { return transformation; }
            set { transformation = value; }
        }

        private List<string> transformationList = new List<string>() { "None", "Log10", "Loge", "Square Root", "ArcSine" };
        public List<string> TransformationList
        {
            get { return transformationList; }
        }

        private ObservableCollection<ColumnInfo> firstCatFactorList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> FirstCatFactorList
        {
            get { return firstCatFactorList; }
            set
            {
                firstCatFactorList = value;
                NotifyPropertyChanged("FirstCatFactorList");
            }
        }

        public string FirstCatFactor
        {
            get
            {
                if (firstCatFactorList.Count == 1)
                    return firstCatFactorList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> secondCatFactorList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> SecondCatFactorList
        {
            get { return secondCatFactorList; }
            set
            {
                secondCatFactorList = value;
                NotifyPropertyChanged("SecondCatFactorList");
            }
        }

        public string SecondCatFactor
        {
            get
            {
                if (secondCatFactorList.Count == 1)
                    return secondCatFactorList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> thirdCatFactorList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> ThirdCatFactorList
        {
            get { return thirdCatFactorList; }
            set
            {
                thirdCatFactorList = value;
                NotifyPropertyChanged("ThirdCatFactorList");
            }
        }

        public string ThirdCatFactor
        {
            get
            {
                if (thirdCatFactorList.Count == 1)
                    return thirdCatFactorList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> fourthCatFactorList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> FourthCatFactorList
        {
            get { return fourthCatFactorList; }
            set
            {
                fourthCatFactorList = value;
                NotifyPropertyChanged("FourthCatFactorList");
            }
        }

        public string FourthCatFactor
        {
            get
            {
                if (fourthCatFactorList.Count == 1)
                    return fourthCatFactorList[0].ColumnName;
                else
                    return null;
            }
        }

        private bool meanSelected = true;
        public bool MeanSelected
        {
            get { return meanSelected; }
            set
            {
                meanSelected = value;
                NotifyPropertyChanged("MeanSelected");
            }
        }
        
        private bool nSelected = true;
        public bool NSelected
        {
            get { return nSelected; }
            set
            {
                nSelected = value;
                NotifyPropertyChanged("NSelected");
            }
        }

        private bool stDevSelected = true;
        public bool StDevSelected
        {
            get { return stDevSelected; }
            set
            {
                stDevSelected = value;
                NotifyPropertyChanged("StDevSelected");
            }
        }
        
        private bool variancesSelected;
        public bool VariancesSelected
        {
            get { return variancesSelected; }
            set
            {
                variancesSelected = value;
                NotifyPropertyChanged("VariancesSelected");
            }
        }
        
        private bool stErrSelected;
        public bool StErrSelected
        {
            get { return stErrSelected; }
            set
            {
                stErrSelected = value;
                NotifyPropertyChanged("StErrSelected");
            }
        }

        private bool minAndMaxSelected;
        public bool MinAndMaxSelected
        {
            get { return minAndMaxSelected; }
            set
            {
                minAndMaxSelected = value;
                NotifyPropertyChanged("MinAndMaxSelected");
            }
        }

        private bool medianQuartileSelected;
        public bool MedianQuartileSelected
        {
            get { return medianQuartileSelected; }
            set
            {
                medianQuartileSelected = value;
                NotifyPropertyChanged("MedianQuartileSelected");
            }
        }

        private bool normalProbabilityPlotSelected;
        public bool NormalProbabilityPlotSelected
        {
            get { return normalProbabilityPlotSelected; }
            set
            {
                normalProbabilityPlotSelected = value;
                NotifyPropertyChanged("NormalProbabilityPlotSelected");
            }
        }

        private bool coefficientVariationSelected;
        public bool CoefficientVariationSelected
        {
            get { return coefficientVariationSelected; }
            set
            {
                coefficientVariationSelected = value;
                NotifyPropertyChanged("CoefficientVariationSelected");
            }
        }

        private bool confidenceLimitsSelected = true;
        public bool ConfidenceLimitsSelected
        {
            get { return confidenceLimitsSelected; }
            set
            {
                confidenceLimitsSelected = value;
                NotifyPropertyChanged("ConfidenceLimitsSelected");
            }
        }

        private int confidenceLimits = 95;
        [CustomValidation(typeof(SummaryStatsGUIValidator), "ValidateConfidenceLimits")]
        public int ConfidenceLimits
        {
            get { return confidenceLimits; }
            set
            {
                confidenceLimits = value;
                NotifyPropertyChanged("ConfidenceLimits");
            }
        }

        private bool byCategoriesAndOverall;
        public bool ByCategoriesAndOverall
        {
            get { return byCategoriesAndOverall; }
            set
            {
                byCategoriesAndOverall = value;
                NotifyPropertyChanged("ByCategoriesAndOverall");
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event, passing the source property that is being updated.
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}